use std::error::Error;
use std::fmt::{Display, Formatter};
use oh_napi_sys::*;
pub type Result<T> = std::result::Result<T, NAPIError>;

#[repr(i32)]
#[derive(Eq, PartialEq, Debug, Clone, Copy)]
pub enum Status {
    Ok = 0,
    InvalidArg,
    ObjectExpected,
    StringExpected,
    NameExpected,
    FunctionExpected,
    NumberExpected,
    BooleanExpected,
    ArrayExpected,
    GenericFailure,
    PendingException,
    Cancelled,
    EscapeCalledTwice,
    HandleScopeMismatch,
    CallbackScopeMismatch,
    /// ThreadSafeFunction queue is full
    QueueFull,
    /// ThreadSafeFunction closed
    Closing,
    BigintExpected,
    DateExpected,
    ArrayBufferExpected,
    DetachableArraybufferExpected,
    WouldDeadlock,
    Unknown = 1024, // unknown status. for example, using napi3 module in napi7 Node.js, and generate an invalid napi3 status
}

impl Display for Status {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        let status_string = format!("{:?}", self);
        write!(f, "{}", status_string)
    }
}

impl AsRef<str> for Status {
    fn as_ref(&self) -> &str {
        match self {
            Status::Ok => "Ok",
            Status::InvalidArg => "InvalidArg",
            Status::ObjectExpected => "ObjectExpected",
            Status::StringExpected => "StringExpected",
            Status::NameExpected => "NameExpected",
            Status::FunctionExpected => "FunctionExpected",
            Status::NumberExpected => "NumberExpected",
            Status::BooleanExpected => "BooleanExpected",
            Status::ArrayExpected => "ArrayExpected",
            Status::GenericFailure => "GenericFailure",
            Status::PendingException => "PendingException",
            Status::Cancelled => "Cancelled",
            Status::EscapeCalledTwice => "EscapeCalledTwice",
            Status::HandleScopeMismatch => "HandleScopeMismatch",
            Status::CallbackScopeMismatch => "CallbackScopeMismatch",
            Status::QueueFull => "QueueFull",
            Status::Closing => "Closing",
            Status::BigintExpected => "BigintExpected",
            Status::DateExpected => "DateExpected",
            Status::ArrayBufferExpected => "ArrayBufferExpected",
            Status::DetachableArraybufferExpected => "DetachableArraybufferExpected",
            Status::WouldDeadlock => "WouldDeadlock",
            _ => "Unknown",
        }
    }
}

impl From<napi_status> for Status {
    fn from(code: napi_status) -> Self {
        match code {
            napi_status_napi_ok => Status::Ok,
            napi_status_napi_invalid_arg => Status::InvalidArg,
            napi_status_napi_object_expected => Status::ObjectExpected,
            napi_status_napi_string_expected => Status::StringExpected,
            napi_status_napi_name_expected => Status::NameExpected,
            napi_status_napi_function_expected => Status::FunctionExpected,
            napi_status_napi_number_expected => Status::NumberExpected,
            napi_status_napi_boolean_expected => Status::BooleanExpected,
            napi_status_napi_array_expected => Status::ArrayExpected,
            napi_status_napi_generic_failure => Status::GenericFailure,
            napi_status_napi_pending_exception => Status::PendingException,
            napi_status_napi_cancelled => Status::Cancelled,
            napi_status_napi_escape_called_twice => Status::EscapeCalledTwice,
            napi_status_napi_handle_scope_mismatch => Status::HandleScopeMismatch,
            napi_status_napi_callback_scope_mismatch => Status::CallbackScopeMismatch,
            napi_status_napi_queue_full => Status::QueueFull,
            napi_status_napi_closing => Status::Closing,
            napi_status_napi_bigint_expected => Status::BigintExpected,
            napi_status_napi_date_expected => Status::DateExpected,
            napi_status_napi_arraybuffer_expected => Status::ArrayBufferExpected,
            napi_status_napi_would_deadlock => Status::WouldDeadlock,
            _ => Status::Unknown,
        }
    }
}

impl From<Status> for napi_status{
    fn from(code: Status) -> Self {
        match code {
            Status::Ok => napi_status_napi_ok,
            Status::InvalidArg => napi_status_napi_invalid_arg,
            Status::ObjectExpected => napi_status_napi_object_expected,
            Status::StringExpected => napi_status_napi_string_expected,
            Status::NameExpected => napi_status_napi_name_expected,
            Status::FunctionExpected => napi_status_napi_function_expected,
            Status::NumberExpected => napi_status_napi_number_expected,
            Status::BooleanExpected => napi_status_napi_boolean_expected,
            Status::ArrayExpected => napi_status_napi_array_expected,
            Status::GenericFailure => napi_status_napi_generic_failure,
            Status::PendingException => napi_status_napi_pending_exception,
            Status::Cancelled => napi_status_napi_cancelled,
            Status::EscapeCalledTwice => napi_status_napi_escape_called_twice,
            Status::HandleScopeMismatch => napi_status_napi_handle_scope_mismatch,
            Status::CallbackScopeMismatch => napi_status_napi_callback_scope_mismatch,
            Status::QueueFull => napi_status_napi_queue_full,
            Status::Closing => napi_status_napi_closing,
            Status::BigintExpected => napi_status_napi_bigint_expected,
            Status::DateExpected => napi_status_napi_date_expected,
            Status::ArrayBufferExpected => napi_status_napi_arraybuffer_expected,
            Status::DetachableArraybufferExpected => napi_status_napi_detachable_arraybuffer_expected,
            Status::WouldDeadlock => napi_status_napi_would_deadlock,
            Status::Unknown => napi_status_napi_generic_failure,
        }
    }
}

#[derive(Debug)]
pub struct  NAPIError {
    pub status: Status,
    pub msg: String
}

impl NAPIError{
    pub fn new(msg: String)->NAPIError{
        NAPIError{
            status: Status::GenericFailure,
            msg,
        }
    }
}

impl Error for NAPIError{}
impl Display for NAPIError {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        write!(f, "NAPI error: [{}]-> {} ", self.status, self.msg)
    }
}






